package utils

import (
	"fmt"
	"sync"
	"time"
)

type CacheItem struct {
	key string
	val any
	exp int64
}

type MemCache struct {
	data       map[string]*CacheItem
	keys2clear []string
	clearAt    int64
	lock       sync.RWMutex
}

func NewMemCache() *MemCache {
	ss := &MemCache{
		data:       make(map[string]*CacheItem),
		keys2clear: make([]string, 0),
	}
	go func(ss *MemCache) {
		for {
			time.Sleep(time.Minute)
			if ss.clearAt > time.Now().Unix() || ss.clearAt == 0 {
				continue
			}
			if !ss.lock.TryLock() {
				continue
			}
			for _, k := range ss.keys2clear {
				delete(ss.data, k)
			}
			ss.keys2clear = make([]string, 0)
			ss.lock.Unlock()
		}
	}(ss)
	return ss
}

func (s *MemCache) Set(k string, v any, exp int64) {
	s.lock.Lock()
	defer s.lock.Unlock()
	item := &CacheItem{
		key: k,
		val: v,
	}
	switch {
	case exp <= 0:
		item.exp = 0
	default:
		item.exp = time.Now().Unix() + exp
		s.clearAt = item.exp
		s.keys2clear = append(s.keys2clear, k)
	}
	s.data[k] = item
}

func (s *MemCache) SetNX(k string, v any, exp int64) error {
	if _, err := s.Get(k); err != nil {
		s.Set(k, v, exp)
		return nil
	}
	return fmt.Errorf(`key已存在`)
}

func (s *MemCache) Get(k string) (any, error) {
	s.lock.RLock()
	defer s.lock.RUnlock()
	if v, ok := s.data[k]; ok {
		if v.exp == 0 {
			return v.val, nil
		}
		if v.exp < time.Now().Unix() {
			delete(s.data, k)
			return v.val, fmt.Errorf(`已过期`)
		}
		return v.val, nil
	}
	return nil, fmt.Errorf(`不存在`)
}

func (s *MemCache) Del(k string) {
	s.lock.Lock()
	defer s.lock.Unlock()
	delete(s.data, k)
}

func (s *MemCache) Clear() {
	s.lock.Lock()
	defer s.lock.Unlock()
	clear(s.data)
	clear(s.keys2clear)
	s.clearAt = 0
}
